package com.xqboss.apps.service.equipment;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.xqboss.apps.constant.Config;
import com.xqboss.apps.constant.LockKey;
import com.xqboss.apps.domain.equipment.*;
import com.xqboss.apps.domain.member.SysUserNft;
import com.xqboss.apps.domain.nft.Nft;
import com.xqboss.apps.domain.user.SysUserWallet;
import com.xqboss.apps.dto.equipment.EquipmentBreakDto;
import com.xqboss.apps.dto.equipment.EquipmentIncomeDto;
import com.xqboss.apps.dto.equipment.EquipmentUpgradeDto;
import com.xqboss.apps.enums.equipment.EquipmentAttrTypeEnum;
import com.xqboss.apps.enums.equipment.EquipmentUpgradeRecordTypeEnum;
import com.xqboss.apps.enums.equipment.UpgradeConfigCoefficientEnum;
import com.xqboss.apps.enums.nft.UserNftStatusEnum;
import com.xqboss.apps.enums.user.UserWalletEnum;
import com.xqboss.apps.enums.user.WalletChangeTypeEnum;
import com.xqboss.apps.service.nft.NftService;
import com.xqboss.apps.service.punch.UserPunchService;
import com.xqboss.apps.service.user.SysUserNftService;
import com.xqboss.apps.service.user.SysUserWalletService;
import com.xqboss.apps.service.user.UserIdentityRankService;
import com.xqboss.apps.vo.equipment.*;
import com.xqboss.common.core.redis.lock.LockValue;
import com.xqboss.common.core.redis.lock.RedisLock;
import com.xqboss.common.core.text.StrFormatter;
import com.xqboss.common.exception.ServiceException;
import com.xqboss.common.utils.TransactionalManage;
import com.xqboss.common.utils.spring.SpringUtils;
import com.xqboss.system.service.ISysConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 装备升级加点模版接口
 *
 * @author wangcong
 * @date 2023-11-23
 */
@Service
public class EquipmentUpgradeService {

    @Autowired
    private SysUserWalletService sysUserWalletService;

    @Autowired
    private ISysConfigService sysConfigService;

    @Autowired
    private EquipmentService equipmentService;

    @Autowired
    private UserPunchService userPunchService;

    @Autowired
    private EquipmentUpgradeConfigService equipmentUpgradeConfigService;

    @Autowired
    private SysUserNftService userNftService;

    @Autowired
    private EquipmentUpgradeConfigCoefficientService equipmentUpgradeConfigCoefficientService;

    @Autowired
    private TransactionalManage transactionalManage;

    @Autowired
    private NftService nftService;

    @Autowired
    private EquipmentUpgradeRecordService equipmentUpgradeRecordService;

    @Autowired
    private UserIdentityRankService userIdentityRankService;

    @Autowired
    private EquipmentUpgradeConfigBreakService equipmentUpgradeConfigBreakService;

    @Autowired
    private EquipmentSmeltTemplateNftService equipmentSmeltTemplateNftService;

    private static final ThreadPoolTaskExecutor threadPoolTaskExecutor = SpringUtils.getBean(ThreadPoolTaskExecutor.class);

    /**
     * 装备预升级
     */
    public EquipmentPreUpgradeVo preUpgrade(Long userId, Long userNftId) {
        Equipment equipment = equipmentService.getByUserNftId(userNftId, userId);
        Nft nft = nftService.getNft(equipment.getNftId());
        EquipmentPreUpgradeVo vo = new EquipmentPreUpgradeVo();
        vo.setEquipment(equipment);
        vo.setMainImg(nft.getMainImg());
        vo.setTitle(nft.getTitle());
        SysUserWallet wallet = sysUserWalletService.getWallet(userId, UserWalletEnum.INTEGRAL);
        vo.setIntegralAmount(wallet.getAmount());
        // 获取装备升级配置
        EquipmentUpgradeConfig upgradeConfig = equipmentUpgradeConfigService.getUpgradeConfigByEquipment(equipment);

        vo.setCurrentLevel(equipment.getLevel());
        vo.setCurrentExp(equipment.getCurrentExp());
        vo.setMaxExp(equipment.getTotalBreakage());
        if (upgradeConfig != null) {
            // 公共参数
            vo.setNextLevel(equipment.getLevel() + 1);

            vo.setModeOneEnable(upgradeConfig.getModeOneEnable());
            vo.setModeTwoEnable(upgradeConfig.getModeTwoEnable());


            // 如果开启模式一升级
            if (upgradeConfig.getModeOneEnable()) {
                vo.setAttributeCount(upgradeConfig.getAttribute());
                vo.setDetectHold(false);
                if (upgradeConfig.getDetectNftId() != null) {
                    vo.setHoldCompetitiveCard(userNftService.checkHoldByNtfId(userId, upgradeConfig.getDetectNftId()));
                } else {
                    vo.setHoldCompetitiveCard(true);
                }
                // 根据配置的风险等级判断是否可以风险锻造
                BigDecimal riskExp = equipment.getTotalBreakage().multiply(upgradeConfig.getMinExpRatio()).setScale(2, RoundingMode.HALF_UP);
                vo.setRiskExp(riskExp);
                vo.setMaxIntegralExpend(calcMaxIntegralExpend(equipment, upgradeConfig));
                vo.setMinIntegralExpend(calcMinIntegralExpend(equipment, upgradeConfig, vo.getMaxIntegralExpend()));
                if (vo.getMinIntegralExpend() > vo.getMaxIntegralExpend()) {
                    // 如果最小消耗值大于最大消耗值，则以最小消耗值为准
                    vo.setMaxIntegralExpend(vo.getMinIntegralExpend());
                }
                //如果当前经验值 >= 风险经验值，则可以风险锻造
                vo.setCanRiskUpgrade(vo.getCurrentExp().compareTo(vo.getRiskExp()) >= 0);
                // 当破损度不够一次打卡时，则为完美锻造
                vo.setCanPerfectUpgrade(equipment.getBreakage().compareTo(sysConfigService.getValue(Config.Sys.Punch.消耗破损值)) < 0);
                //装备支持【特权升级】后台配置无视风险、无视瑕疵
                boolean privilegeUpgrade = equipmentUpgradeConfigService.checkPrivilegeUpgrade(upgradeConfig.getId());
                if (privilegeUpgrade) {
                    //如果是特权升级，则风险升级和完美升级都为false
                    vo.setCanRiskUpgrade(false);
                    vo.setCanPerfectUpgrade(false);
                }
                vo.setCanPrivilegeUpgrade(privilegeUpgrade);
            } else if (upgradeConfig.getModeTwoEnable()) {
                // 模式2零经验可以突破
                if (upgradeConfig.getMinExpRatio().compareTo(BigDecimal.ZERO) == 0) {
                    vo.setCanPrivilegeUpgrade(true);
                    //如果是特权升级，则风险升级和完美升级都为false
                    vo.setCanRiskUpgrade(false);
                    vo.setCanPerfectUpgrade(false);
                } else {
                    vo.setCanPrivilegeUpgrade(false);
                }
                vo.setAttributeCount(equipment.getRemainAttrCount());
            }
//            vo.setDetectHold(false);
//            if (upgradeConfig.getDetectNftId() != null) {
//                vo.setHoldCompetitiveCard(userNftService.checkHoldByNtfId(userId, upgradeConfig.getDetectNftId()));
//            } else {
//                vo.setHoldCompetitiveCard(true);
//            }
//            vo.setNextLevel(equipment.getLevel() + 1);
//            // 根据配置的风险等级判断是否可以风险锻造
//            BigDecimal riskExp = equipment.getTotalBreakage().multiply(upgradeConfig.getMinExpRatio()).setScale(2, RoundingMode.HALF_UP);
//            vo.setRiskExp(riskExp);
//            // 如果当前等级的装备开启模式一升级 可加点数为模式一配置的属性加点数，否则为当前装备的剩余可加点数
//            if (upgradeConfig.getModeTwoEnable()) {
//                vo.setAttributeCount(equipment.getRemainAttrCount());
//            } else {
//                vo.setAttributeCount(upgradeConfig.getAttribute());
//            }
//            if (upgradeConfig.getModeOneEnable()) {
//                vo.setMaxIntegralExpend(calcMaxIntegralExpend(equipment, upgradeConfig));
//                vo.setMinIntegralExpend(calcMinIntegralExpend(equipment, upgradeConfig, vo.getMaxIntegralExpend()));
//                if(vo.getMinIntegralExpend() > vo.getMaxIntegralExpend()){
//                    // 如果最小消耗值大于最大消耗值，则以最小消耗值为准
//                    vo.setMaxIntegralExpend(vo.getMinIntegralExpend());
//                }
//            }
//            //如果当前经验值 >= 风险经验值，则可以风险锻造
//            vo.setCanRiskUpgrade(vo.getCurrentExp().compareTo(vo.getRiskExp()) >= 0);
//            // 当破损度不够一次打卡时，则为完美锻造
//            vo.setCanPerfectUpgrade(equipment.getBreakage().compareTo(sysConfigService.getValue(Config.Sys.Punch.消耗破损值)) < 0);
//            //装备支持【特权升级】后台配置无视风险、无视瑕疵
//            boolean privilegeUpgrade = equipmentUpgradeConfigService.checkPrivilegeUpgrade(upgradeConfig.getId());
//            if (privilegeUpgrade) {
//                //如果是特权升级，则风险升级和完美升级都为false
//                vo.setCanRiskUpgrade(false);
//                vo.setCanPerfectUpgrade(false);
//            }
//            vo.setCanPrivilegeUpgrade(privilegeUpgrade);
//            vo.setModeOneEnable(upgradeConfig.getModeOneEnable());
//            vo.setModeTwoEnable(upgradeConfig.getModeTwoEnable());
        } else {
            upgradeConfig = equipmentUpgradeConfigService.getUpgradeConfigByEquipmentMaxLevel(equipment);
            vo.setDetectHold(false);
            vo.setHoldCompetitiveCard(true);
            vo.setAttributeCount(0);
            vo.setMinIntegralExpend(0);
            vo.setMaxIntegralExpend(0);
            vo.setCanRiskUpgrade(false);
            vo.setCanPerfectUpgrade(false);
            vo.setCanPrivilegeUpgrade(false);
            if (Objects.nonNull(upgradeConfig)) {
                vo.setModeOneEnable(upgradeConfig.getModeOneEnable());
                vo.setModeTwoEnable(upgradeConfig.getModeTwoEnable());
                vo.setAttributeCount(equipment.getRemainAttrCount());
            } else {
                vo.setModeOneEnable(true);
                vo.setModeTwoEnable(false);
            }
        }
        //设置融入折扣的等级和系数
        vo.setDiscountLevel(sysConfigService.getValue(Config.Sys.Nft.融入折扣_身份等级));
        vo.setDiscountRatio(sysConfigService.getValue(Config.Sys.Nft.融入折扣_系数));
        vo.setDiscountEquipmentLevel(sysConfigService.getValue(Config.Sys.Nft.融入折扣_装备等级));
        vo.setIsNew(equipmentService.checkEquipmentIsNew(equipment.getId()));
        // 获取当前装备剩余的打卡次数以及打卡收益
        vo.setCurrentIncome(userPunchService.calcPunchIncomeByWeaken(equipment, false));
        vo.setCurrentPunchCount(userPunchService.calcPunchCountByBreakage(equipment.getBreakage()));
        // 是否可熔炼
        vo.setIsSmelt(equipmentSmeltTemplateNftService.lambdaQuery().eq(EquipmentSmeltTemplateNft::getNftId, equipment.getNftId()).count() > 0 ? 1 : 0);
        return vo;
    }

    /**
     * 装备升级(此方法为装备升级模式一)
     */
    @RedisLock(lockKey = LockKey.LOCK_EQUIPMENT_UPGRADE)
    public EquipmentUpgradeVo upgrade(@LockValue Long userId, EquipmentUpgradeDto dto) throws Exception {
        SysUserNft userNft = userNftService.getById(dto.getUserNftId());
        if (userNft == null || !Objects.equals(userId, userNft.getUserId())) {
            throw new ServiceException("装备不存在！");
        }
        if (Objects.isNull(dto.getIsPrivilege())) {
            dto.setIsPrivilege(false);
        }
        Equipment equipment = equipmentService.getByNftNumberId(userNft.getNftNumberId());
        // 是否是风险锻造？风险锻造判断条件为：剩余破损度 > 打卡破损消耗值
        // 获取装备升级配置
        EquipmentUpgradeConfig upgradeConfig = equipmentUpgradeConfigService.getUpgradeConfigByEquipment(equipment);
        if (upgradeConfig == null) {
            throw new ServiceException("装备已满级");
        }
        //校验装备升级配置中模式一是否开启
        if (!upgradeConfig.getModeOneEnable()) {
            throw new ServiceException("当前装备不支持模式一升级");
        }
        // 如果需要进行藏品检测
        if (upgradeConfig.getDetectNftId() != null) {
            if (!userNftService.checkHoldByNtfId(userId, upgradeConfig.getDetectNftId())) {
                String nftTitle = nftService.getNft(upgradeConfig.getDetectNftId()).getTitle();
                throw new ServiceException("拥有" + nftTitle + "才能继续升级");
            }
        }
        // 判断当前身份能否升级
        boolean detectMaxLevel = (equipment.getLevel() + 1) <= userIdentityRankService.getMaxEquipUpLevelByUserId(userId);
        if (!detectMaxLevel) {
            throw new ServiceException("超过当前身份段位最大升级等级");
        }

        //如果是特权升级，判断用户是否有特权
        boolean isPrivilege = dto.getIsPrivilege();
        if (isPrivilege) {
            //1、判断当前装备是否满足特权升级
            if (!equipmentUpgradeConfigService.checkPrivilegeUpgrade(upgradeConfig.getId())) {
                throw new ServiceException("当前升级装备瑕疵概率不为1，不支持特权升级");
            }
            //2、设置风险升级为false
            dto.setIsRisk(false);
        }
        //检测用户是否可以折扣
        boolean isDiscount = false;
        if (dto.getDiscountIntegralExpend() != null
                && new BigDecimal(dto.getIntegralExpend()).compareTo(dto.getDiscountIntegralExpend()) > 0) {
            //检测当前装备等级是否大于等于后台系统配置的融入折扣_装备等级
            if (equipment.getLevel() < sysConfigService.getValue(Config.Sys.Nft.融入折扣_装备等级)) {
                throw new ServiceException("当前装备等级不支持折扣升级");
            }
            //检测装备是否全新的装备
            if (!equipmentService.checkEquipmentIsNew(equipment.getId())) {
                throw new ServiceException("当前装备不是全新装备，不支持折扣升级");
            }
            //检测用户是否可以折扣
            boolean b = userIdentityRankService.checkPrivilege(userId);
            if (!b) {
                throw new ServiceException("当前身份不支持折扣升级");
            }
            //后端计算的折扣价格
            BigDecimal sysPrice = new BigDecimal(dto.getIntegralExpend()).multiply(sysConfigService.getValue(Config.Sys.Nft.融入折扣_系数));
            //如果折扣价格小于最小消耗热豆值，则折扣价格为最小消耗热豆值
            int minIntegralExpend = calcMinIntegralExpend(equipment, upgradeConfig, calcMaxIntegralExpend(equipment, upgradeConfig));
            if (sysPrice.compareTo(new BigDecimal(minIntegralExpend)) < 0) {
                dto.setDiscountIntegralExpend(new BigDecimal(minIntegralExpend));
            } else {
                //满足折扣，对比计算热豆（实际消耗=原消耗*折扣%）
                if (dto.getDiscountIntegralExpend().compareTo(sysPrice) != 0) {
                    throw new ServiceException("折扣融入热豆数量不准确，请重新计算");
                }
            }
            isDiscount = true;
        }

        // 判断是否可以升级
        BigDecimal currentExp = equipment.getCurrentExp();
        BigDecimal maxExp = equipment.getTotalBreakage();
        BigDecimal riskExp = equipment.getTotalBreakage().multiply(upgradeConfig.getMinExpRatio()).setScale(2, RoundingMode.HALF_UP);
        //如果是特权升级，无视经验值
        if (currentExp.compareTo(riskExp) < 0 && !isPrivilege) {
            //当前经验值小于风险经验时无法进行升级
            throw new ServiceException(StrFormatter.format("经验达到{}才能风险升级", riskExp));
        }
        // 根据配置的风险等级判断是否可以风险锻造
        boolean isRisk = equipment.getBreakage().compareTo(sysConfigService.getValue(Config.Sys.Punch.消耗破损值)) >= 0;
        if (!Objects.equals(isRisk, dto.getIsRisk()) && !isPrivilege) {
            throw new ServiceException("当前锻造类型不一致，请刷新重试！");
        }
        int maxAttribute = upgradeConfig.getAttribute();
        int actualAttribute;

        // 前置校验
        // 判断装备状态
        checkEquipmentStatus(userNft);
        // 判断加点数是否正确
        if (maxAttribute != dto.getLuck() + dto.getTalent() + dto.getDurable() + dto.getStrong()) {
            throw new ServiceException("属性分配完毕才能升级");
        }
        // 判断消耗热豆是否合理
        int maxIntegralExpend = calcMaxIntegralExpend(equipment, upgradeConfig);
        int minIntegralExpend = calcMinIntegralExpend(equipment, upgradeConfig, maxIntegralExpend);
        if (minIntegralExpend > maxIntegralExpend) {
            // 如果最小消耗值大于最大消耗值，则以最小消耗值为准
            maxIntegralExpend = minIntegralExpend;
        }
        if (dto.getIntegralExpend() < minIntegralExpend || dto.getIntegralExpend() > maxIntegralExpend) {
            throw new ServiceException(StrFormatter.format("热豆熔入范围：{}-{}", minIntegralExpend, maxIntegralExpend));
        }

        // 1. 判断是风险升级还是完美升级
        BigDecimal totalAttr = new BigDecimal(maxAttribute);
        if (isRisk) {
            //1. 风险升级
            // 最大属性加点 × 当前经验占比 × 瑕疵概率
            EquipmentUpgradeConfigCoefficient blemishCoefficient = equipmentUpgradeConfigCoefficientService.randomCoefficient(UpgradeConfigCoefficientEnum.BLEMISH, upgradeConfig);
            actualAttribute = totalAttr
//                    .multiply(currentExp.divide(maxExp, 10, RoundingMode.HALF_UP))
                    .multiply(blemishCoefficient.getProbability())
                    .setScale(0, RoundingMode.HALF_UP)
                    .max(BigDecimal.ONE)
                    .min(totalAttr)
                    .intValue();
        } else {
            //2. 完美升级
            // 直接获得最大属性加点
            actualAttribute = maxAttribute;
        }

        // 计算用户的加点分配逻辑， 然后根据占比进行排序
        List<Pair<EquipmentAttrTypeEnum, BigDecimal>> attrList = new ArrayList<>();
        attrList.add(new Pair<>(EquipmentAttrTypeEnum.LUCK, new BigDecimal(dto.getLuck()).divide(totalAttr, 10, RoundingMode.HALF_UP)));
        attrList.add(new Pair<>(EquipmentAttrTypeEnum.STRONG, new BigDecimal(dto.getStrong()).divide(totalAttr, 10, RoundingMode.HALF_UP)));
        attrList.add(new Pair<>(EquipmentAttrTypeEnum.DURABLE, new BigDecimal(dto.getDurable()).divide(totalAttr, 10, RoundingMode.HALF_UP)));
        attrList.add(new Pair<>(EquipmentAttrTypeEnum.TALENT, new BigDecimal(dto.getTalent()).divide(totalAttr, 10, RoundingMode.HALF_UP)));

        // 对列表按照占比大小进行排序，高占比在前边
        attrList = attrList.stream().sorted((f, s) -> {
            int c = f.getValue().compareTo(s.getValue());
            if (c == 0) {
                // 如果两个的占比一致，则随机排序
                return RandomUtil.randomBoolean() ? 1 : -1;
            }
            return -c;
        }).collect(Collectors.toList());

        // 开始分配: 按照比例先四舍五入进行分配，
        Map<EquipmentAttrTypeEnum, Integer> attrAddMap = new HashMap<>();
        BigDecimal actualAttr = new BigDecimal(actualAttribute);
        int remainAttr = actualAttribute;
        for (Pair<EquipmentAttrTypeEnum, BigDecimal> attr : attrList) {
            int value = attr.getValue().multiply(actualAttr).setScale(0, RoundingMode.HALF_UP).intValue();
            value = Math.min(value, remainAttr);
            attrAddMap.put(attr.getKey(), value);
            remainAttr = remainAttr - value;
        }
        // 剩余的点数按照比例从大到小一点一点分配
        while (remainAttr > 0) {
            for (Pair<EquipmentAttrTypeEnum, BigDecimal> attr : attrList) {
                attrAddMap.put(attr.getKey(), attrAddMap.get(attr.getKey()) + 1);
                remainAttr = remainAttr - 1;
                if (remainAttr <= 0) {
                    break;
                }
            }
        }


        // 重新计算装备新的破损度总值和耐久度总值
        int strong = equipment.getStrong() + attrAddMap.get(EquipmentAttrTypeEnum.STRONG);
        int durable = equipment.getDurable() + attrAddMap.get(EquipmentAttrTypeEnum.DURABLE);
        // 破损度 = 坚固值 * 破损倍率
        BigDecimal totalBreakageNew = BigDecimal.valueOf(strong).multiply(sysConfigService.getValue(Config.Sys.Punch.破损度倍数)).setScale(2, RoundingMode.HALF_UP);
        // 耐久度 = 耐久值 * 耐久倍数
        BigDecimal totalDurabilityNew = BigDecimal.valueOf(durable).multiply(sysConfigService.getValue(Config.Sys.Punch.耐久度倍数)).setScale(2, RoundingMode.HALF_UP);

        // 剩余破损度、剩余耐久度
        // 剩余耐久度 = 升级后耐久度总数 － 升级前耐久度消耗
        BigDecimal currentDurability = totalDurabilityNew.subtract(equipment.getTotalDurability().subtract(equipment.getDurability()));
        // 剩余破损度
        BigDecimal currentBreakage;
        if (Objects.equals(dto.getIntegralExpend(), maxIntegralExpend)) {
            // 如果热豆消耗满了， 则直接将破损度修复满
            currentBreakage = totalBreakageNew;
        } else {
            // 剩余破损度 =  （升级前破损值总数 × 破损修复保底系数－升级前剩余破损值）+ 升级前剩余破损值 ＋ 【升级后破损总数 － （升级前破损值总数 × 破损修复保底系数）】× 实际消耗热豆占比 × 破损修复比例
            //注：（升级前破损值总数 × 破损修复保底系数－升级前剩余破损值）计算出来为负数时算为0

            //（升级前破损值总数 × 破损修复保底系数－升级前剩余破损值）
            // 破损修复爆率
            EquipmentUpgradeConfigCoefficient breakageCoefficient = equipmentUpgradeConfigCoefficientService.randomCoefficient(UpgradeConfigCoefficientEnum.BREAKAGE, upgradeConfig);
            // 热豆消耗占比
            double integralRate = dto.getIntegralExpend() * 1.0 / maxIntegralExpend;
            currentBreakage = (equipment.getTotalBreakage().multiply(upgradeConfig.getBreakageGuarantee()).subtract(equipment.getBreakage())).max(BigDecimal.ZERO)
                    // + 升级前剩余破损值
                    .add(equipment.getBreakage())
                    .add(
                            // 升级后破损总数  - （升级前破损值总数 × 破损修复保底系数）
                            totalBreakageNew.subtract(equipment.getTotalBreakage().multiply(upgradeConfig.getBreakageGuarantee()))
                                    .multiply(new BigDecimal(integralRate))
                                    .multiply(breakageCoefficient.getProbability())
                    )
                    .setScale(0, RoundingMode.HALF_UP)
                    .max(equipment.getBreakage())
                    .min(totalBreakageNew);
        }

        Equipment newEquipment = new Equipment();
        newEquipment.setId(equipment.getId());
        // 重置等级收入
        newEquipment.setLevelIncome(BigDecimal.ZERO);
        newEquipment.setCurrentExp(BigDecimal.ZERO);
        newEquipment.setIncomeRatio(equipment.getIncomeRatio());

        newEquipment.setLevel(equipment.getLevel() + 1);
        newEquipment.setTotalBreakage(totalBreakageNew);
        newEquipment.setTotalDurability(totalDurabilityNew);
        newEquipment.setBreakage(currentBreakage);
        newEquipment.setDurability(currentDurability);

        newEquipment.setTotalAttrCount(equipment.getTotalAttrCount() + actualAttribute);
        newEquipment.setLuck(equipment.getLuck() + attrAddMap.get(EquipmentAttrTypeEnum.LUCK));
        newEquipment.setDurable(equipment.getDurable() + attrAddMap.get(EquipmentAttrTypeEnum.DURABLE));
        newEquipment.setStrong(equipment.getStrong() + attrAddMap.get(EquipmentAttrTypeEnum.STRONG));
        newEquipment.setTalent(equipment.getTalent() + attrAddMap.get(EquipmentAttrTypeEnum.TALENT));

        //校验升级前后当前装备的打卡收益，如果升级后的收益小于升级前的收益，则不允许升级
        BigDecimal currentIncome = userPunchService.calcPunchIncomeByWeaken(equipment, false);
        BigDecimal newIncome = userPunchService.calcPunchIncomeByWeaken(newEquipment, false);
        if (newIncome.compareTo(currentIncome) <= 0) {
            throw new ServiceException("升级后的空投可能小于等于升级前的空投，请重新分配加点");
        }

        // 存储升级记录
        EquipmentUpgradeRecord record = new EquipmentUpgradeRecord();
        record.setUserId(userId);
        record.setNftId(equipment.getNftId());
        record.setUserNftId(userNft.getId());
        record.setNftNumberId(userNft.getNftNumberId());
        record.setUpgradeConfigId(upgradeConfig.getId());
        record.setEquipmentId(equipment.getId());
        record.setEquipmentAfter(JSON.toJSONString(newEquipment));
        record.setEquipmentBefore(JSON.toJSONString(equipment));
        record.setDurable(attrAddMap.get(EquipmentAttrTypeEnum.DURABLE));
        record.setLuck(attrAddMap.get(EquipmentAttrTypeEnum.LUCK));
        record.setStrong(attrAddMap.get(EquipmentAttrTypeEnum.STRONG));
        record.setTalent(attrAddMap.get(EquipmentAttrTypeEnum.TALENT));

        record.setIntegralExpend(BigDecimal.valueOf(dto.getIntegralExpend()));
        record.setAddAttribute(actualAttribute);
        record.setLevelBefore(equipment.getLevel());
        record.setLevelAfter(newEquipment.getLevel());
        record.setType(dto.getType());
        record.setDiscountIntegralExpend(dto.getDiscountIntegralExpend());

        boolean finalIsDiscount = isDiscount;
        boolean execute = transactionalManage.executeWithException(() -> {
            // 存储记录
            equipmentUpgradeRecordService.saveRecord(record);
            // 扣除热豆
            sysUserWalletService.reduce(userId, UserWalletEnum.INTEGRAL, finalIsDiscount ? dto.getDiscountIntegralExpend() : new BigDecimal(dto.getIntegralExpend()), WalletChangeTypeEnum.EQUIPMENT_UPGRADE, 0L, "装备升级消耗");
            // 更新装备
            equipmentService.updateAttribute(newEquipment);
            return true;
        });


        EquipmentUpgradeVo vo = new EquipmentUpgradeVo();
        vo.setNftId(userNft.getNftId());
        vo.setUserNftId(userNft.getId());
        vo.setAttributeCount(actualAttribute);
        vo.setLuck(attrAddMap.get(EquipmentAttrTypeEnum.LUCK));
        vo.setDurable(attrAddMap.get(EquipmentAttrTypeEnum.DURABLE));
        vo.setStrong(attrAddMap.get(EquipmentAttrTypeEnum.STRONG));
        vo.setTalent(attrAddMap.get(EquipmentAttrTypeEnum.TALENT));
        vo.setIntegralExpend(dto.getIntegralExpend());
        vo.setIsRisk(isRisk);
        vo.setIsPrivilege(isPrivilege);
        vo.setDiscountIntegralExpend(dto.getDiscountIntegralExpend());
        vo.setEquipmentBefore(equipment);
        vo.setEquipmentAfter(newEquipment);
        return vo;
    }

    /**
     * 计算装备最小消耗热豆值
     *
     * @param equipment     装备信息
     * @param upgradeConfig 升级配置
     * @return
     */
    public int calcMinIntegralExpend(Equipment equipment, EquipmentUpgradeConfig upgradeConfig, Integer maxIntegralExpend) {
        //-计算最小消耗热豆值
        //【（实际产出 × 最小平衡系数 ）＋ {（升级前破损值总数 × 破损修复保底系数－升级前剩余破损值）  ÷  单次破损消耗×（幸运值 × 收益折算系数） }】× {（1－天赋值 ÷ 总属性） × 天赋折算系数）}
        //注：（升级前破损值总数 × 破损修复保底系数－升级前剩余破损值）计算出来为负数时算为0
        BigDecimal integralMinExp = upgradeConfig.getIntegralMinExp();
        if (integralMinExp == null || integralMinExp.compareTo(new BigDecimal(maxIntegralExpend)) > 0) {
            integralMinExp = BigDecimal.ZERO;
        }
        return equipment.getLevelIncome().multiply(upgradeConfig.getIntegralMinRatio())
                .add(
                        equipment.getTotalBreakage().multiply(upgradeConfig.getBreakageGuarantee()).subtract(equipment.getBreakage()).max(BigDecimal.ZERO)
                                .divide(sysConfigService.getValue(Config.Sys.Punch.消耗破损值), 10, RoundingMode.HALF_UP)
                                .multiply(userPunchService.calcPunchIncome(equipment, false))
                )
                .multiply(BigDecimal.ONE.subtract(new BigDecimal(equipment.getTalent()).divide(new BigDecimal(equipment.getTotalAttrCount()), 10, RoundingMode.HALF_UP)).multiply(upgradeConfig.getTalentCoefficient()))
                .max(integralMinExp)
                .setScale(0, RoundingMode.HALF_UP)
                .intValue();
    }

    /**
     * 计算装备最大消耗热豆值
     *
     * @param equipment
     * @return
     */
    public int calcMaxIntegralExpend(Equipment equipment, EquipmentUpgradeConfig upgradeConfig) {
        //-计算最大消耗热豆值
        // 最大产出 × 最大平衡系数 × {（1－天赋值 ÷ 总属性） × 天赋折算系数）}
        // 最大产出 = 单次收益 * 装备可打卡次数
        BigDecimal income = userPunchService.calcPunchIncome(equipment, false);
        BigDecimal maxPunchCount = equipment.getTotalBreakage().divide(sysConfigService.getValue(Config.Sys.Punch.消耗破损值), 0, RoundingMode.DOWN);
        return income.multiply(maxPunchCount)
                .multiply(upgradeConfig.getIntegralMaxRatio())
                .multiply(BigDecimal.ONE.subtract(
                                new BigDecimal(equipment.getTalent()).divide(new BigDecimal(equipment.getTotalAttrCount()), 10, RoundingMode.HALF_UP))
                        .multiply(upgradeConfig.getTalentCoefficient()))
                .setScale(0, RoundingMode.HALF_UP).intValue();
    }

    /**
     * 估算装备收益计算
     *
     * @param params
     * @return
     */
    public EquipmentIncomeVo estimateIncome(Long userId, EquipmentIncomeDto params) {
        SysUserNft userNft = userNftService.getById(params.getUserNftId());
        if (Objects.isNull(userNft) || !Objects.equals(userId, userNft.getUserId())) {
            throw new ServiceException("该装备不存在！");
        }
        // 原始装备信息
        Equipment equipment = equipmentService.getByNftNumberId(userNft.getNftNumberId());

        // 重新计算装备新的破损度总值和耐久度总值
        int strong = equipment.getStrong() + params.getStrong();
        int durable = equipment.getDurable() + params.getDurable();

        //重新换算 总的 耐久度与破损度
        // 破损度 = 坚固值 * 破损倍率
        BigDecimal totalBreakageNew = BigDecimal.valueOf(strong).multiply(sysConfigService.getValue(Config.Sys.Punch.破损度倍数)).setScale(2, RoundingMode.HALF_UP);
        // 耐久度 = 耐久值 * 耐久倍数
        BigDecimal totalDurabilityNew = BigDecimal.valueOf(durable).multiply(sysConfigService.getValue(Config.Sys.Punch.耐久度倍数)).setScale(2, RoundingMode.HALF_UP);

        // 重新计算 剩余的 耐久度与破损度
        // 剩余耐久度 = 升级后耐久度总数 － 升级前消耗的耐久度
        BigDecimal currentDurability = totalDurabilityNew.subtract(equipment.getTotalDurability().subtract(equipment.getDurability()));
        // 剩余破损度 = 升级后破损总数 - 升级前消耗的破损度
        BigDecimal currentBreakage = totalBreakageNew.subtract(equipment.getTotalBreakage().subtract(equipment.getBreakage()));
        // 装备总属性加的点数 = 耐久 + 破损 + 幸运 + 天赋
        int actualAttribute = params.getDurable() + params.getStrong() + params.getLuck() + params.getTalent();
        // 升级后的装备
        Equipment newEquipment = new Equipment();
        newEquipment.setId(equipment.getId());
        newEquipment.setIncomeRatio(equipment.getIncomeRatio());
        newEquipment.setTotalBreakage(totalBreakageNew);
        newEquipment.setTotalDurability(totalDurabilityNew);
        newEquipment.setBreakage(currentBreakage);
        newEquipment.setDurability(currentDurability);
        newEquipment.setTotalAttrCount(equipment.getTotalAttrCount() + actualAttribute);
        newEquipment.setLuck(equipment.getLuck() + params.getLuck());
        newEquipment.setDurable(equipment.getDurable() + params.getDurable());
        newEquipment.setStrong(equipment.getStrong() + params.getStrong());
        newEquipment.setTalent(equipment.getTalent() + params.getTalent());

        // 获取新装备最大空投产出(预估收益)
        BigDecimal estimateIncome = userPunchService.calcPunchIncomeByWeaken(newEquipment, false);
        // 获取新装备打卡次数
        int estimatePunchCount = userPunchService.calcPunchCountByBreakage(newEquipment.getBreakage());

        EquipmentIncomeVo vo = new EquipmentIncomeVo();
        vo.setEstimateIncome(estimateIncome);
        vo.setEstimatePunchCount(estimatePunchCount);
        return vo;
    }

    /**
     * 根据用户装备id获取装备信息，计算装备的推荐加点
     * 公式：
     * 每次打卡消耗耐久=耐久消耗系数*总点数
     * 剩余耐久百分比 =（总耐久-每次打卡消耗耐久*4）/ 总耐久
     * 剩余耐久百分比不低于90为最佳耐久加点
     *
     * @param equipmentId
     * @return
     */
    public EquipmentIncomeDto recommendPoint(Long equipmentId) {
        // 获取装备信息 判空
        Equipment equipment = equipmentService.getById(equipmentId);
        if (Objects.isNull(equipment)) {
            throw new ServiceException("装备不存在！");
        }
        // 计算装备的推荐加点
        // 1. 计算装备的总属性加点
        int totalAttr = equipment.getTotalAttrCount();
        // 2. 计算装备的推荐加点
        int luck = totalAttr / 4;
        int strong = totalAttr / 4;
        int durable = totalAttr / 4;
        int talent = totalAttr - luck - strong - durable;
        EquipmentIncomeDto dto = new EquipmentIncomeDto();
        dto.setLuck(luck);
        dto.setStrong(strong);
        dto.setDurable(durable);
        dto.setTalent(talent);
        return dto;

    }

    /**
     * 装备预突破（获取当前装备等级区间的突破材料）
     *
     * @param userId
     * @param userNftId
     * @return
     */
    public EquipmentPreBreakVo preBreak(Long userId, Long userNftId) {
        Equipment equipment = equipmentService.getByUserNftId(userNftId, userId);
        Nft nft = nftService.getNft(equipment.getNftId());
        EquipmentPreBreakVo vo = new EquipmentPreBreakVo();
        vo.setTitle(nft.getTitle());
        vo.setMainImg(nft.getMainImg());

        // 获取装备升级配置
        EquipmentUpgradeConfig upgradeConfig = equipmentUpgradeConfigService.getUpgradeConfigByEquipment(equipment);

        int breakCount = (int) equipmentUpgradeRecordService.getBreakCountByUserIdAndEquipmentId(userId, equipment.getId(), equipment.getLevel());
        vo.setBreakNum(breakCount + 1);
        vo.setCurrentLevel(equipment.getLevel());
        if (Objects.nonNull(upgradeConfig)) {
            vo.setNextLevel(equipment.getLevel() + 1);
            vo.setAttributeCount(equipment.getRemainAttrCount() + upgradeConfig.getAttribute());
            // 获取装备升级配置等级区间的突破材料
            List<EquipmentUpgradeConfigBreak> breakMaterials = equipmentUpgradeConfigBreakService.getBreakMaterialsByConfigId(upgradeConfig.getId());
            vo.setBreakMaterials(BeanUtil.copyToList(breakMaterials, EquipmentUpgradeConfigBreakVo.class));
            vo.setBreakHotBeans(upgradeConfig.getBreakHotBeans());
        } else {
            vo.setAttributeCount(0);
        }
        // 校验用户是否拥有突破材料
        List<EquipmentUpgradeConfigBreak> needMaterials = equipmentUpgradeConfigBreakService.getBreakMaterialsByConfigId(upgradeConfig.getId());
        if (CollUtil.isNotEmpty(needMaterials)) {
            boolean b = userNftService.checkMaterials(userId, needMaterials);
            vo.setIsMaterials(b);
        }
        // 突破的最高等级
        vo.setMaxLevel(equipmentUpgradeConfigService.getMaxLevelByEquipment(equipment));
        return vo;
    }

    /**
     * 装备预进化
     *
     * @param userId
     * @return
     */
    public EquipmentPreBreakVo preEvolve(Long userId, EquipmentIncomeDto params) {
        Equipment equipment = equipmentService.getByUserNftId(params.getUserNftId(), userId);
        EquipmentPreBreakVo vo = new EquipmentPreBreakVo();
        // 计算预估收益（升级后产出）
        EquipmentIncomeVo incomeVo = estimateIncome(userId, params);
        vo.setEstimate(incomeVo);
        // 升级前剩余产出
        BigDecimal currentIncome = userPunchService.calcPunchIncomeByWeaken(equipment, false);
        // 获取装备升级配置 进化热豆消耗系数
        EquipmentUpgradeConfig upgradeConfig = equipmentUpgradeConfigService.getUpgradeConfigByEquipmentMaxLevel(equipment);
        // 计算进化消耗热豆 ＝ （升级后剩余产出 － 升级前剩余产出）× 后台进化热豆消耗系数, 并四舍五入保留两位小数
        BigDecimal evolveHotBeans = (incomeVo.getEstimateIncome().subtract(currentIncome))
                .multiply(upgradeConfig.getEvolveHotBeansRatio()).setScale(2, RoundingMode.HALF_UP);
        vo.setEvolveHotBeans(evolveHotBeans);
        return vo;
    }

    /**
     * 2.0装备突破 (只升等级并分配总的可加点数，不分配到具体的天赋上)
     * 【突破按钮】：突破经验不足时按钮置灰，单击提示【经验不足，无法突破】、满足经验按钮点亮，单击按钮弹出【确认突破弹窗】
     * 确认后校验用户消耗材料和消耗热豆是否都足够，不足toast提示【突破所需材料不足】，若满足突破条件则消耗热豆开始突破，且弹出【正在突破的动画】
     * 突破按成功失败随机概率
     * 突破失败弹出，仅扣除热豆，显示当前装备当前等级已突破总次数和未消耗的熔炼材料及数量
     *
     * @param userId
     * @param dto
     * @return
     */
    @RedisLock(lockKey = LockKey.LOCK_EQUIPMENT_BREAK)
    public EquipmentBreakVo breakMaterials(@LockValue Long userId, EquipmentBreakDto dto) throws Exception {
        SysUserNft userNft = userNftService.getById(dto.getUserNftId());
        if (userNft == null || !Objects.equals(userId, userNft.getUserId())) {
            throw new ServiceException("装备不存在！");
        }
        // 当前装备
        Equipment equipment = equipmentService.getByNftNumberId(userNft.getNftNumberId());
        // 获取装备升级配置
        EquipmentUpgradeConfig upgradeConfig = equipmentUpgradeConfigService.getUpgradeConfigByEquipment(equipment);
        if (Objects.isNull(upgradeConfig)) {
            throw new ServiceException("装备已满级");
        }
        int actualAttribute = upgradeConfig.getAttribute();
        //校验装备升级配置中模式二是否开启
        if (!upgradeConfig.getModeTwoEnable()) {
            throw new ServiceException("当前装备不支持模式二突破");
        }
        // 前置校验 判断装备状态
        checkEquipmentStatus(userNft);
        // 判断当前装备经验值是否满足突破条件
        // 装备当前经验值
        BigDecimal currentExp = equipment.getCurrentExp();
        // 装备风险经验值
        BigDecimal riskExp = equipment.getTotalBreakage().multiply(upgradeConfig.getMinExpRatio()).setScale(2, RoundingMode.HALF_UP);
        //如果是特权升级，无视经验值
        if (currentExp.compareTo(riskExp) < 0) {
            //当前经验值小于风险经验时无法进行突破
            throw new ServiceException(StrFormatter.format("经验达到{}才能突破", riskExp));
        }
        // 判断消耗热豆是否合理
        if (dto.getBreakHotBeans().compareTo(upgradeConfig.getBreakHotBeans()) != 0) {
            throw new ServiceException(StrFormatter.format("热豆消耗范围：{}", upgradeConfig.getBreakHotBeans()));
        }
        // 校验用户热豆余额是否足够
        SysUserWallet wallet = sysUserWalletService.getWallet(userId, UserWalletEnum.INTEGRAL);
        if (Objects.isNull(wallet) || wallet.getAmount().compareTo(dto.getBreakHotBeans()) < 0) {
            throw new ServiceException("热豆余额不足");
        }
        // 校验用户是否拥有突破材料
        List<EquipmentUpgradeConfigBreak> needMaterials = equipmentUpgradeConfigBreakService.getBreakMaterialsByConfigId(upgradeConfig.getId());
        if (CollUtil.isNotEmpty(needMaterials)) {
            boolean b = userNftService.checkMaterials(userId, needMaterials);
            if (!b) {
                throw new ServiceException("突破所需材料不足");
            }
        }
        // 突破次数
        int breakCount = (int) equipmentUpgradeRecordService.getBreakCountByUserIdAndEquipmentId(userId, equipment.getId(), equipment.getLevel());
        // 突破按成功失败随机概率
        boolean breakthrough = breakthrough(breakCount, upgradeConfig.getBreakProtectNum(), upgradeConfig.getBreakSuccessRatio());
        // 如果配置有保底次数，则没有达到保底次数突破成功了也是失败
//        if (upgradeConfig.getBreakProtectNum() != null && breakCount < upgradeConfig.getBreakProtectNum()) {
//            breakthrough = false;
//        }

        // 新装备
        Equipment newEquipment = new Equipment();
        newEquipment.setId(equipment.getId());
        // 重置等级收入
        newEquipment.setLevelIncome(BigDecimal.ZERO);
        newEquipment.setCurrentExp(BigDecimal.ZERO);
        newEquipment.setLevel(equipment.getLevel() + 1);
        newEquipment.setRemainAttrCount(equipment.getRemainAttrCount() + actualAttribute);

        // 生成突破记录
        EquipmentUpgradeRecord breakRecord = new EquipmentUpgradeRecord();
        breakRecord.setUserId(userId);
        breakRecord.setUserNftId(userNft.getId());
        breakRecord.setNftId(equipment.getNftId());
        breakRecord.setNftNumberId(userNft.getNftNumberId());
        breakRecord.setEquipmentId(equipment.getId());
        breakRecord.setUpgradeConfigId(upgradeConfig.getId());
        breakRecord.setAddAttribute(actualAttribute);
        breakRecord.setIntegralExpend(dto.getBreakHotBeans());
        breakRecord.setLevelBefore(equipment.getLevel());
        breakRecord.setLevelAfter(newEquipment.getLevel());
        breakRecord.setIsBreak(breakthrough);
        breakRecord.setEquipmentBefore(JSON.toJSONString(equipment));
        breakRecord.setEquipmentAfter(JSON.toJSONString(newEquipment));
        breakRecord.setType(EquipmentUpgradeRecordTypeEnum.BREAK);
        breakRecord.setDurable(0);
        breakRecord.setLuck(0);
        breakRecord.setStrong(0);
        breakRecord.setTalent(0);
        // 存储突破记录
        equipmentUpgradeRecordService.saveRecord(breakRecord);

        //无论突破成功还是失败，都要扣除热豆
        sysUserWalletService.reduce(userId, UserWalletEnum.INTEGRAL, dto.getBreakHotBeans(), WalletChangeTypeEnum.BREAK_CONSUME, breakRecord.getId(), WalletChangeTypeEnum.BREAK_CONSUME.getMsg());

        // 返回的vo对象
        EquipmentBreakVo vo = new EquipmentBreakVo();
        vo.setBreakNum(breakCount + 1);
        vo.setIsBreak(breakthrough);
        if (breakthrough) {
            // 突破成功
            vo.setCurrentLevel(equipment.getLevel() + 1);
            vo.setAttributeCount(actualAttribute);
            // 更新装备等级以及剩余总加点数
            equipmentService.updateAttribute(newEquipment);
            // 销毁用户的藏品
            if (CollUtil.isNotEmpty(needMaterials)) {
                threadPoolTaskExecutor.execute(() -> {
                    needMaterials.forEach(item -> destroyNft(userId, item.getMaterialId(), item.getNum()));
                });
            }
        } else {
            //突破失败
            vo.setBreakMaterials(BeanUtil.copyToList(needMaterials, EquipmentUpgradeConfigBreakVo.class));
        }
        return vo;
    }

    /**
     * 突破 按数量销毁用户的藏品/装备 [ 提前校验用户背包中是否有足够的材料 ]
     *
     * @param userId
     * @param nftId
     * @param num
     */
    private void destroyNft(Long userId, Long nftId, int num) {
        List<SysUserNft> userNftList = userNftService.getUserNftByUserIdAndNftId(userId, nftId);
        // 按数量销毁用户的藏品
        List<Long> ids = userNftList.stream()
                .map(SysUserNft::getId)  // 将SysUserNft对象转换为其id
                .limit(num)  // 取出前num个元素
                .collect(Collectors.toList());  // 转换为List
        userNftService.userNftBurn(userId, ids, UserNftStatusEnum.EQUIPMENT_BREAK);
    }

    /**
     * 2.0装备进化
     *
     * @param userId
     * @param dto
     * @return
     */
    @RedisLock(lockKey = LockKey.LOCK_EQUIPMENT_EVOLVE)
    public Map<String, Object> evolve(@LockValue Long userId, EquipmentIncomeDto dto) throws Exception {
        if (Objects.isNull(dto) || Objects.isNull(dto.getEvolveHotBeans())
                || dto.getEvolveHotBeans().compareTo(BigDecimal.ZERO) < 0) {
            throw new ServiceException("进化消耗材料不合法");
        }
        SysUserNft userNft = userNftService.getById(dto.getUserNftId());
        if (userNft == null || !Objects.equals(userId, userNft.getUserId())) {
            throw new ServiceException("装备不存在！");
        }
        // 当前老装备
        Equipment equipment = equipmentService.getByNftNumberId(userNft.getNftNumberId());
        // 获取装备升级配置
//        EquipmentUpgradeConfig upgradeConfig = equipmentUpgradeConfigService.getUpgradeConfigByEquipment(equipment);
//        if (Objects.isNull(upgradeConfig)) {
//            throw new ServiceException("装备已满级");
//        }
        //校验装备升级配置中模式二是否开启
//        if (!upgradeConfig.getModeTwoEnable()) {
//            throw new ServiceException("当前装备不支持模式二进化");
//        }
        // 前置校验 判断装备状态
        checkEquipmentStatus(userNft);
        // 装备总属性加的点数 = 耐久 + 破损 + 幸运 + 天赋
        int actualAttribute = equipment.getRemainAttrCount();
        // 如果没有剩余加点数无法进化
        if (actualAttribute <= 0) {
            throw new ServiceException("剩余加点数不足");
        }
        int currentAttr = dto.getLuck() + dto.getTalent() + dto.getDurable() + dto.getStrong();
        // 如果当前加点大于装备剩余总加点数无法进化
        if (currentAttr > actualAttribute) {
            throw new ServiceException("进化加点数大于总加点数");
        }
        // 校验用户余额是否足够
        SysUserWallet wallet = sysUserWalletService.getWallet(userId, UserWalletEnum.INTEGRAL);
        if (Objects.isNull(wallet) || wallet.getAmount().compareTo(dto.getEvolveHotBeans()) < 0) {
            throw new ServiceException("进化所需材料不足");
        }

        // 重新计算装备新的破损度总值和耐久度总值
        int strong = equipment.getStrong() + dto.getStrong();
        int durable = equipment.getDurable() + dto.getDurable();
        //重新换算 总的 耐久度与破损度
        // 破损度 = 坚固值 * 破损倍率
        BigDecimal totalBreakageNew = BigDecimal.valueOf(strong).multiply(sysConfigService.getValue(Config.Sys.Punch.破损度倍数)).setScale(2, RoundingMode.HALF_UP);
        // 耐久度 = 耐久值 * 耐久倍数
        BigDecimal totalDurabilityNew = BigDecimal.valueOf(durable).multiply(sysConfigService.getValue(Config.Sys.Punch.耐久度倍数)).setScale(2, RoundingMode.HALF_UP);
        // 重新计算 剩余的 耐久度与破损度
        // 剩余耐久度 = 升级后耐久度总数 － 升级前消耗的耐久度
        BigDecimal currentDurability = totalDurabilityNew.subtract(equipment.getTotalDurability().subtract(equipment.getDurability()));
        // 剩余破损度 = 升级后破损总数 - 升级前消耗的破损度
        BigDecimal currentBreakage = totalBreakageNew.subtract(equipment.getTotalBreakage().subtract(equipment.getBreakage()));
        // 升级后的装备
        Equipment newEquipment = new Equipment();
        newEquipment.setId(equipment.getId());
        // 重置等级收入
//        newEquipment.setLevelIncome(BigDecimal.ZERO);
//        newEquipment.setCurrentExp(BigDecimal.ZERO);

//        newEquipment.setLevel(equipment.getLevel() + 1);
        newEquipment.setBreakage(currentBreakage);
        newEquipment.setTotalBreakage(totalBreakageNew);
        newEquipment.setDurability(currentDurability);
        newEquipment.setTotalDurability(totalDurabilityNew);
        newEquipment.setIncomeRatio(equipment.getIncomeRatio());

        newEquipment.setTotalAttrCount(equipment.getTotalAttrCount() + currentAttr);
        newEquipment.setLuck(equipment.getLuck() + dto.getLuck());
        newEquipment.setDurable(equipment.getDurable() + dto.getDurable());
        newEquipment.setStrong(equipment.getStrong() + dto.getStrong());
        newEquipment.setTalent(equipment.getTalent() + dto.getTalent());
        // 进化成功从新分配余加点数（原剩余加点 - 此次进化总加点）
        newEquipment.setRemainAttrCount(equipment.getRemainAttrCount() - currentAttr);

        //校验升级前后当前装备的打卡收益，如果升级后的收益小于升级前的收益，则不允许升级
        BigDecimal currentIncome = userPunchService.calcPunchIncomeByWeaken(equipment, false);
        BigDecimal newIncome = userPunchService.calcPunchIncomeByWeaken(newEquipment, false);
        if (newIncome.compareTo(currentIncome) <= 0) {
            throw new ServiceException("进化后的空投小于等于进化前的空投，建议使用一键加点进化");
        }
        // 生成进化记录
        EquipmentUpgradeRecord evolveRecord = new EquipmentUpgradeRecord();
        evolveRecord.setUserId(userId);
        evolveRecord.setUserNftId(userNft.getId());
        evolveRecord.setNftId(equipment.getNftId());
        evolveRecord.setNftNumberId(userNft.getNftNumberId());
        evolveRecord.setEquipmentId(equipment.getId());
        evolveRecord.setUpgradeConfigId(0L);
        evolveRecord.setAddAttribute(currentAttr);
        evolveRecord.setIntegralExpend(dto.getEvolveHotBeans());
        evolveRecord.setLevelBefore(0);
        evolveRecord.setLevelAfter(0);
        evolveRecord.setDurable(dto.getDurable());
        evolveRecord.setLuck(dto.getLuck());
        evolveRecord.setStrong(dto.getStrong());
        evolveRecord.setTalent(dto.getTalent());
        evolveRecord.setEquipmentBefore(JSON.toJSONString(equipment));
        evolveRecord.setEquipmentAfter(JSON.toJSONString(newEquipment));
        evolveRecord.setType(EquipmentUpgradeRecordTypeEnum.EVOLVE);
        // 存储进化记录
        equipmentUpgradeRecordService.saveRecord(evolveRecord);
        // 扣除热豆
        sysUserWalletService.reduce(userId, UserWalletEnum.INTEGRAL, dto.getEvolveHotBeans(), WalletChangeTypeEnum.EVOLVE_CONSUME, evolveRecord.getId(), WalletChangeTypeEnum.EVOLVE_CONSUME.getMsg());
        // 更新装备加点值与破损度
        equipmentService.updateAttribute(newEquipment);
        Map<String, Object> vo = new HashMap<>();
        vo.put("currentIncome", currentIncome);
        vo.put("newIncome", newIncome);
        vo.put("currentPunch", userPunchService.calcPunchCountByBreakage(equipment.getBreakage()));
        vo.put("newPunch", userPunchService.calcPunchCountByBreakage(newEquipment.getBreakage()));
        return vo;
    }

    /**
     * 概率方法
     *
     * @param attemptNumber 计次
     * @param protectNum    保底次数
     * @param successRatio  成功概率
     * @return
     */
    public boolean breakthrough(int attemptNumber, int protectNum, BigDecimal successRatio) {
        if (attemptNumber >= protectNum) {
            return true;
        }
        BigDecimal randomValue = BigDecimal.valueOf(Math.random());
        return randomValue.compareTo(successRatio) < 0;
    }

    /**
     * 检测装备状态是否在闲置中
     *
     * @param userNft
     * @throws ServiceException
     */
    public void checkEquipmentStatus(SysUserNft userNft) throws ServiceException {
        if (userNft.getNftStatus() == UserNftStatusEnum.LOCK) {
            throw new ServiceException("装备已锁定，无法突破");
        }
        if (userNft.getNftStatus() == UserNftStatusEnum.USED) {
            throw new ServiceException("装备打卡中，不能升级哦");
        }
        if (userNft.getNftStatus() == UserNftStatusEnum.SALEABLE) {
            throw new ServiceException("装备出售中，不能升级哦");
        }
        if (userNft.getNftStatus() != UserNftStatusEnum.NORMAL) {
            throw new ServiceException("装备不可用！");
        }
    }

}
